package cn.xu.tool.big.a.compments.transaction;

import cn.xu.tool.big.a.compments.strategy.TradingStrategy;
import cn.xu.tool.big.a.dto.KLine;
import cn.xu.tool.big.a.dto.TotalReturn;
import cn.xu.tool.big.a.dto.TradeRecord;

import java.util.ArrayList;
import java.util.List;

/**
 * 类名: TransactionTest
 * 描述: TODO
 * 作者: ClarkXu clark2021@qq.com
 * 日期: 2025/7/14
 */
public class TransactionCp3 {

    public static List<TradeRecord> doTransactionBt(List<KLine> kLines, String stockCode, String startDate, String endDate,
                                                    double initialCapital, double commissionRate,
                                                    TotalReturn totalReturn, int shortPeriod, int longPeriod) throws Exception {
        // 3. 生成交易信号
        int[] signals = TradingStrategy.generateSignals(kLines, shortPeriod, longPeriod);

        // 4. 回测
        return backtest(kLines, signals, initialCapital, commissionRate, totalReturn, false, 0.998, 1.01);
    }

    public static List<TradeRecord> backtest(List<KLine> kLines, int[] signals,
                                             double initialCapital, double commissionRate,
                                             TotalReturn totalReturn, boolean allSell,
                                             double buyDiscount, double sellDiscount) {
        double capital = initialCapital;
        int totalPosition = 0;
        double totalCost = 0; // 新增：记录累计持仓成本
        double lastBuyPrice = 0;
        double lastSellPrice = 0; // 新增：记录上一次卖出价格
        boolean isFirstBuy = true; // 新增：标记是否为首次买入
        int tradeCount = 0;
        List<TradeRecord> records = new ArrayList<>();
        System.out.println("日期\t\t\t操作\t价格\t\t成交量\t\t仓位\t\t现金\t\t总资产");
        for (int i = 0; i < kLines.size(); i++) {
            KLine kLine = kLines.get(i);
            int signal = signals[i];
            double price = kLine.getClose();

            // 买入逻辑：增加买入价限制条件
            if (signal == 1 && capital > 0) {
                // 首次买入无限制，后续买入需满足价差条件
                boolean canBuy = isFirstBuy || (price <= lastSellPrice * buyDiscount); // 低于前次卖出价3%

                if (canBuy) {
                    double amount = capital * (1 - commissionRate);
                    int tmpPosition = (int) (amount / price);
                    tmpPosition = tmpPosition - tmpPosition % 100;
                    if (tmpPosition >= 100) {
                        totalPosition += tmpPosition;
                        capital -= tmpPosition * price;
                        lastBuyPrice = price * (1 + commissionRate);
                        tradeCount++;
                        isFirstBuy = false; // 首次买入完成后更新标记
                        System.out.printf("%s\t买入\t%.2f\t%d\t%d\t%.2f\t%.2f\n",
                                kLine.getDate(), price, tmpPosition, totalPosition, capital, capital + totalPosition * price);
                        records.add(TradeRecord.create(kLine.getDate(), "买入", price, totalPosition, capital,
                                capital + totalPosition * price, tmpPosition));
                        totalCost += tmpPosition * price; // 新增：累加持仓成本
                    }
                }
                // 不满足买入条件时记录状态
                else if (!isFirstBuy) {
                    double total = capital + totalPosition * price;
                    records.add(TradeRecord.create(kLine.getDate(), "买入跳过(价差不足)", price,
                            totalPosition, capital, total, 0));
                }
            }
            // 卖出逻辑（保留原有止盈条件）
            else if (signal == -1 && totalPosition > 0) {
                // 计算平均持仓成本（总成本/总股数）
                double averageCost = totalCost / totalPosition;
                double minSellPrice = averageCost * sellDiscount; // 要求高出持仓成本一定比例

                if (price >= minSellPrice) {
                    double value = totalPosition * price;
                    capital += value * (1 - commissionRate);
                    lastSellPrice = price; // 记录本次卖出价格
                    int tmpVolume = totalPosition;
                    totalPosition = 0;
                    tradeCount++;
                    System.out.printf("%s\t卖出\t%.2f\t%d\t%d\t%.2f\t%.2f\n",
                            kLine.getDate(), price, totalPosition, totalPosition, capital, capital);
                    records.add(TradeRecord.create(kLine.getDate(), "卖出", price, totalPosition, capital, capital, tmpVolume));
                    totalCost = 0; // 新增：卖出后重置持仓成本
                } else {
                    double total = capital + totalPosition * price;
                    records.add(TradeRecord.create(kLine.getDate(), "持仓（盈利未达标）", price,
                            totalPosition, capital, total, 0));
                }
            }
            // 无信号时记录持仓
            else if (totalPosition > 0) {
                double total = capital + totalPosition * price;
                records.add(TradeRecord.create(kLine.getDate(), "持仓", price, totalPosition, capital, total, 0));
            }
        }

        boolean doAllSell = false;
        if (totalPosition > 0) {
            KLine last = kLines.get(kLines.size() - 1);
            double price = last.getClose();
            double value = totalPosition * price;
            capital += value * (1 - commissionRate);
            if(allSell){
                // 强制平仓逻辑
                System.out.printf("%s\t强制平仓\t%.2f\t%d\t%d\t%.2f\t%.2f\n",
                        last.getDate(), price, totalPosition, totalPosition, capital, capital);
                records.add(TradeRecord.create(last.getDate(), "强制平仓", price, 0, capital, capital, totalPosition));
                totalPosition = 0;
                totalCost = 0; // 新增：平仓后重置持仓成本
                doAllSell = true;
            }else{
                //预测计算出下次卖出的最低价，不得低于持仓成本乘以sellDiscount
                calculateMinSell(totalCost, totalPosition, sellDiscount);
            }
        }else{
            //预测计算出下次买入的最高价，不得高于上次卖出价格乘以buyDiscount
            calculateMaxBuy(lastSellPrice, buyDiscount);
        }

        // 计算总收益
        double finalReturn = capital - initialCapital;
        double returnRate = finalReturn / initialCapital * 100;
        System.out.println("期初股价: " + kLines.get(0).getClose());
        System.out.println("期末股价: " + kLines.get(kLines.size()-1).getClose());
        System.out.println("初始资金: " + initialCapital);
        if(doAllSell||totalPosition==0){
            System.out.println("最终资金: " + capital);
        }else{
            System.out.println("期末仓位(股): " + totalPosition + " * 最新收盘价换算 -> 最终资金: " + capital + " 持仓成本: "+(totalCost / totalPosition));
        }
        System.out.println("总收益率: " + String.format("%.2f", returnRate) + "%");
        System.out.println("交易次数: " + tradeCount);
        totalReturn.setInitialCapital(initialCapital);
        totalReturn.setFinalCapital(capital);
        totalReturn.setReturnRate(String.format("%.2f", returnRate) + "%");
        totalReturn.setTradeCount(tradeCount);
        return records;
    }

    // 新增方法：计算下次卖出的最低价格（不低于持仓成本×sellDiscount）
    private static double calculateMinSell(double totalCost, int totalPosition, double sellDiscount) {
        if (totalPosition <= 0) return 0.0;

        double averageCost = totalCost / totalPosition;
        double minSellPrice = averageCost * sellDiscount;
        System.out.printf("预测下次卖出的最低价格：%.3f (持仓成本×%.3f)\n", minSellPrice, sellDiscount);
        return minSellPrice;
    }

    // 新增方法：计算下次买入的最高价格（不高于上次卖出价×buyDiscount）
    private static double calculateMaxBuy(double lastSellPrice, double buyDiscount) {
        if (lastSellPrice <= 0) return Double.MAX_VALUE;

        double maxBuyPrice = lastSellPrice * buyDiscount;
        System.out.printf("预测下次买入的最高价格：%.3f (前次卖出价×%.3f)\n", maxBuyPrice, buyDiscount);
        return maxBuyPrice;
    }
}
